package com.gzc;

import com.mongodb.*;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.CreateCollectionOptions;
import com.mongodb.client.model.Filters;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.UuidRepresentation;
import org.bson.codecs.UuidCodec;
import org.bson.codecs.configuration.CodecRegistries;
import org.bson.codecs.configuration.CodecRegistry;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Updates.inc;
import static org.bson.codecs.configuration.CodecRegistries.fromRegistries;

/**
 * MongoDB的JAVA驱动官方文档，3.4版本
 * <p>
 * <a href="http://mongodb.github.io/mongo-java-driver/3.4/driver/getting-started/quick-start/">URL</a>
 *
 * @author Spirit_wolf
 * @date 2018/9/21
 */
public class MongoDBDriverQuickStart {
    public MongoDBDriverQuickStart() {
        //Connect to MongoDB，Use MongoClient() to make a connection to a running MongoDB instance.
        MongoClientURI connectionString = new MongoClientURI("mongodb://localhost:27017");
        MongoClient mongoClient = new MongoClient(connectionString);

        //指定数据库,如果不存在则会新建该库
        MongoDatabase database = mongoClient.getDatabase("mydb");
        //指定集合,如果不存在则新建该集合
        // NOTE:MongoCollection instances are immutable.MongoCollection实例是不可变对象
        MongoCollection<Document> collection = database.getCollection("test");
        //明确的指明要创建的集合
        {
            //数据库里不存在该集合时正常执行，已有该名字的集合再执行createCollection函数则报错
            database.createCollection("cappedCollection",
                    new CreateCollectionOptions().capped(true).sizeInBytes(0x100000));
        }
        //Get A List of Collections
        {
            //所有集合名字
            for (String name : database.listCollectionNames()) {
                System.out.printf("集合名字=%s\n", name);
            }
        }
        //Drop a Collection
        {
            //You can drop a collection by using the MongoCollection.drop() method:
            database.
                    getCollection("cappedCollection").
                    drop();
        }
        //Get A List of Collections
        {
            //所有集合名字
            for (String name : database.listCollectionNames()) {
                System.out.printf("集合名字=%s\n", name);
            }
        }
        //Immutability不可变对象
        {
//            MongoDatabase and MongoCollection instances are immutable.
        }
//        CodecRegistry注册Codec
        {
//            An overload of the getCollection method allows clients to specify a different
//            class for representing BSON documents. For example, users of the legacy CRUD API from the 2.x driver series
//            may wish to continue using BasicDBObject in order to ease the transition to the new CRUD API:
//            重载getCollection方法允许客户指定一个不同的类代表BSON文档。
//            例如,用户从2.x遗留CRUD的API。
//            2.x系列驱动程序可能希望继续使用BasicDBObject为了缓解过渡到新的CRUD API:
            CodecRegistry(database);
//            There are two requirements that must be met for any class used in this way:
//            a Codec for it must be registered in the MongoCollection’s CodecRegistry
//            the Codec must be one that encodes and decodes a full BSON document (and not just, for example, a single BSON value like an Int32)
//            任何类以这种方式使用,必须满足两个条件:
//            一个编解码器必须在MongoCollection的CodecRegistry里注册
//            编解码器必须编码和解码一个完整的BSON文档(而不仅仅是像Int32的单个BSON值)

//            By default, a MongoCollection is configured with Codecs for three classes:
//            Document
//            BasicDBObject
//            BsonDocument

//            Applications, however, are free to register Codec implementations for other classes by customizing the CodecRegistry. New CodecRegistry instances are configurable at three levels:
//            In a MongoClient via MongoClientOptions
//            In a MongoDatabase via its withCodecRegistry method
//            In a MongoCollection via its withCodecRegistry method

//            Consider the case of encoding and decoding instances of the UUID class. The Java driver by default encodes instances of UUID using a byte ordering that is not compatible with other MongoDB drivers, and changing the default would be quite dangerous. But it is possible for new applications that require interoperability across multiple drivers to be able to change that default, and they can do that with a CodecRegistry.
            //UUID的Java驱动程序默认编码实例使用的字节序与其他MongoDB驱动程序不兼容,并且改变默认UUID是很危险的。
            //但有可能新需要互操作的应用程序跨多个司机能够改变这种默认情况下,他们可以做,CodecRegistry。
            //有跨多个不同的客户端驱动程序之间交互的需求，你可以用CodecRegistry来改变默认的UUID驱动,如下面的例子：
            CodecRegistry2();

//            Readers and Writers
//            http://mongodb.github.io/mongo-java-driver/3.4/bson/readers-and-writers/
        }


        Document doc = CreateADocument();
        //增删改查
        InsertOneDocument(collection, doc);
        InsertMultipleDocuments(collection);

        CountDocumentsInACollection(collection);

//        Read Operations
//        http://mongodb.github.io/mongo-java-driver/3.4/driver/tutorials/perform-read-operations/
        FindFirstDocument(collection);
        FindAllDocumentsInACollection(collection);
        GetASingleDocumentThatMatchesAFilter(collection);
        GetAllDocumentsThatMatchAFilter(collection);

//        Write Operations (Insert, Update, Replace, Delete)
//        http://mongodb.github.io/mongo-java-driver/3.4/driver/tutorials/perform-write-operations/
        UpdateASingleDocument(collection);
        UpdateMultipleDocuments(collection);

        DeleteASingleDocumentThatMatchAFilter(collection);
        DeleteAllDocumentsThatMatchAFilter(collection);
        //创建索引
        CreateIndexes(collection);
    }

    private void CodecRegistry(MongoDatabase database) {
        // Pass BasicDBObject.class as the second argument
        MongoCollection<BasicDBObject> collection = database.getCollection("mycoll", BasicDBObject.class);

        // insert a document
        BasicDBObject document = new BasicDBObject("x", 1);
        collection.insertOne(document);
        document.append("x", 2).append("y", 3);

        // replace a document
        collection.replaceOne(Filters.eq("_id", document.get("_id")), document);

        // find documents
        List<BasicDBObject> foundDocument = collection.find().into(new ArrayList<BasicDBObject>());
    }

    private void CodecRegistry2() {
        // Replaces the default UuidCodec with one that uses the new standard UUID representation
        CodecRegistry codecRegistry =
                fromRegistries(CodecRegistries.fromCodecs(new UuidCodec(UuidRepresentation.STANDARD)),
                        MongoClient.getDefaultCodecRegistry());

        // globally
        MongoClientOptions options = MongoClientOptions.builder()
                .codecRegistry(codecRegistry).build();
        MongoClient client = new MongoClient(new ServerAddress(), options);

        // or per database
        MongoDatabase database = client.getDatabase("mydb")
                .withCodecRegistry(codecRegistry);

        // or per collection
        MongoCollection<Document> collection = database.getCollection("mycoll")
                .withCodecRegistry(codecRegistry);
    }

    /**
     * 新建一个文档
     *
     * @return
     */
    private Document CreateADocument() {
        //创建文档格式如下
//        {
//            "name" : "MongoDB",
//                "type" : "database",
//                "count" : 1,
//                "versions": [ "v3.2", "v3.0", "v2.6" ],
//            "info" : { x : 203, y : 102 }
//        }
        Document doc = new Document("name", "MongoDB")
                .append("type", "database")
                .append("count", 1)
                .append("versions", Arrays.asList("v3.2", "v3.0", "v2.6"))
                .append("info", new Document("x", 203).append("y", 102));
        return doc;
//        NOTE：
//        The BSON type of array corresponds to the Java type java.util.List. For a list of the BSON type and the corresponding type in Java, see .
//        文档值是数组对应JAVA里的List集合类型
//        Documents
//        http://mongodb.github.io/mongo-java-driver/3.4/bson/documents/
    }

    /**
     * 插入一个文档
     *
     * @param collection
     * @param doc
     */
    private void InsertOneDocument(MongoCollection<Document> collection, Document doc) {
        //插入一个文档
        collection.insertOne(doc);
//        NOTE
//        If no top-level _id field is specified in the document, MongoDB automatically adds the _id field to the inserted document.
//        _id字段会自动插入到文档里，_id字段是不可变的，不允许修改，但是可以替换（替换现有文档）
//        The replacement document can have different fields from the original document. In the replacement document,
//        you can omit the _id field since the _id field is immutable; however, if you do include the _id field,
//        you cannot specify a different value for the _id field.
    }

    /**
     * 插入多个文档
     *
     * @param collection
     */
    private void InsertMultipleDocuments(MongoCollection<Document> collection) {
        //插入文档格式如下
//        { "i" : value }
//        Create the documents in a loop and add to the documents list:
        List<Document> documents = new ArrayList<Document>();
        for (int i = 0; i < 100; i++) {
            documents.add(new Document("i", i));
        }
//        To insert these documents to the collection, pass the list of documents to the insertMany() method.
        //插入多个文档
        collection.insertMany(documents);
//        NOTE
//        If no top-level _id field is specified in the document, MongoDB automatically adds the _id field to the inserted document.
        //        _id字段会自动插入到文档里
    }

    /**
     * 集合里文档的数量
     *
     * @param collection
     */
    private void CountDocumentsInACollection(MongoCollection<Document> collection) {
        System.out.println(collection.countDocuments());
    }

    /**
     * 查出集合里的第一个文档
     *
     * @param collection
     */
    private void FindFirstDocument(MongoCollection<Document> collection) {
        Document myDoc = collection.find().first();
        System.out.println(myDoc.toJson());
//      查出如下文档
//        {
//            "_id" : { "$oid" : "551582c558c7b4fbacf16735" },
//            "name" : "MongoDB",
//                "type" : "database",
//                "count" : 1,
//                "info" : { "x" : 203, "y" : 102 }
//        }
//        NOTE
//        The _id element has been added automatically by MongoDB to your document and your value will differ from that shown. MongoDB reserves field names that start with "_" and "$" for internal use.
        //注意_id字段的值
    }

    /**
     * 查出集合里的所有文档
     *
     * @param collection
     */
    private void FindAllDocumentsInACollection(MongoCollection<Document> collection) {
        MongoCursor<Document> cursor = collection.find().iterator();
        try {
            while (cursor.hasNext()) {
                Document doc = cursor.next();
                System.out.println(doc.toJson());
            }
        } finally {
            cursor.close();
        }

        System.out.println("================cursor end================");
        for (Document cur : collection.find()) {
            System.out.println(cur.toJson());
        }
    }

    /**
     * 查出一个文档
     *
     * @param collection
     */
    private void GetASingleDocumentThatMatchesAFilter(MongoCollection<Document> collection) {
        Document myDoc = collection.find(eq("i", 71)).first();
        System.out.println(myDoc.toJson());
        //查出文档如下
//        { "_id" : { "$oid" : "5515836e58c7b4fbc756320b" }, "i" : 71 }
    }

    /**
     * 查出多个文档
     *
     * @param collection
     */
    private void GetAllDocumentsThatMatchAFilter(MongoCollection<Document> collection) {
        Block<Document> printBlock = new Block<Document>() {
            @Override
            public void apply(final Document document) {
                System.out.println(document.toJson());
            }
        };
        System.out.println("==============查出i的值大于50的文档==============");
        //查出i的值大于50的文档
        collection.find(gt("i", 50)).forEach(printBlock);

        System.out.println("===============查出i的值大于50并且小于等于100的文档=====================");
        //查出i的值大于50并且小于等于100的文档
        collection.find(and(gt("i", 50), lte("i", 100))).forEach(printBlock);
    }

    /**
     * 通过查询条件，更新一个文档
     *
     * @param collection
     */
    private void UpdateASingleDocument(MongoCollection<Document> collection) {
//        The following example updates the first document that meets the filter i equals 10 and sets the value of i to 110:
        //更新第一个文档，查出来的i的值等于10的文档，如果查不出来则新插入一个文档设置i的值为110
        collection.updateOne(eq("i", 10), new Document("$set", new Document("i", 110)));
    }

    /**
     * 通过查询条件，更新多个文档
     *
     * @param collection
     */
    private void UpdateMultipleDocuments(MongoCollection<Document> collection) {
//        The following example increments the value of i by 100 for all documents where =i is less than 100:
        //更新所有i的值小于100的文档，将文档i的值增加100
        UpdateResult updateResult = collection.updateMany(lt("i", 100), inc("i", 100));
        System.out.println(updateResult.getModifiedCount());
    }

    /**
     * 通过查询条件，删除一个文档
     *
     * @param collection
     */
    private void DeleteASingleDocumentThatMatchAFilter(MongoCollection<Document> collection) {
//        To delete at most a single document that match the filter, use the deleteOne method:
//        The following example deletes at most one document that meets the filter i equals 110:
        collection.deleteOne(eq("i", 110));
    }

    /**
     * 通过查询条件，删除多个文档
     *
     * @param collection
     */
    private void DeleteAllDocumentsThatMatchAFilter(MongoCollection<Document> collection) {
//        To delete all documents matching the filter use the deleteMany method.
//        The following example deletes all documents where i is greater or equal to 100:
        DeleteResult deleteResult = collection.deleteMany(gte("i", 100));
        System.out.println(deleteResult.getDeletedCount());
    }

    /**
     * 创建索引
     *
     * @param collection
     */
    private void CreateIndexes(MongoCollection<Document> collection) {
//        To create an index on a field or fields, pass an index specification document to the createIndex() method.
//        An index key specification document contains the fields to index and the index type for each field:
//        new Document(<field1>, <type1>).append(<field2>, <type2>) ...
//        For an ascending index type, specify 1 for <type>.
//        For a descending index type, specify -1 for <type>.
//        The following example creates an ascending index on the i field:
        //默认是升序索引
        collection.createIndex(new Document("i", 1));
//        Create Indexes
//        http://mongodb.github.io/mongo-java-driver/3.4/driver/tutorials/indexes/

//        //升序索引
//        collection.createIndex(Indexes.ascending("name"));
//        //复合升序索引
//        collection.createIndex(Indexes.ascending("stars", "name"));
//        //降序索引
//        collection.createIndex(Indexes.descending("stars"));
//        //复合降序索引
//        collection.createIndex(Indexes.descending("stars", "name"));
//        //指定字段的升降序，创建复合索引
//        collection.createIndex(Indexes.compoundIndex(Indexes.descending("stars"), Indexes.ascending("name")));

//        Use the listIndexes() method to get a list of indexes.
        for (Document index : collection.listIndexes()) {
            System.out.println(index.toJson());
        }
    }

}
